package org.dreamwork.cai.core.impl;

import org.dreamwork.cai.SRFException;
import org.dreamwork.cai.ServiceNotFoundException;
import org.dreamwork.cai.core.IRemoteServices;
import org.dreamwork.cai.io.IRemoteServiceStub;
import org.dreamwork.cai.core.RemoteServiceFactory;
import org.dreamwork.cai.srf.MethodInfo;
import org.dreamwork.cai.srf.ServiceRouteFrame;
import org.dreamwork.cai.srf.ServiceRouteItem;
import org.dreamwork.cai.srf.ServiceRouteTable;
import org.dreamwork.cai.tools.Const;
import org.dreamwork.cai.tools.Misc;
import org.dreamwork.concurrent.broadcast.ILocalBroadcastService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.rmi.RemoteException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public abstract class AbstractRemoteServices implements IRemoteServices {
    private final Logger logger = LoggerFactory.getLogger (AbstractRemoteServices.class);

    protected ServiceRouteTable table;
    protected ILocalBroadcastService broadcast;
    protected boolean autowireSupported = false;

    public boolean isAutowireSupported () {
        return autowireSupported;
    }

    public void setAutowireSupported (boolean autowireSupported) {
        this.autowireSupported = autowireSupported;
    }

    public void setServiceRouteTable (ServiceRouteTable table) {
        this.table = table;
    }

    public void setBroadcast (ILocalBroadcastService broadcast) {
        this.broadcast = broadcast;
    }

    protected void patch (ServiceRouteFrame srf) throws ClassNotFoundException, NoSuchMethodException, SRFException {
        Set<ServiceRouteItem> copy = new HashSet<> (srf.items.values ());
        srf.items.clear ();
        for (ServiceRouteItem src : copy) {
            ServiceRouteItem sri = new ServiceRouteItem (src.name, srf);

            Class<?>[] types = new Class<?> [src.types.length];
            for (int i = 0; i < types.length; i ++) {
                types[i] = Misc.translate (src.types[i]);
            }
            sri.setTypes (Arrays.asList (types));
            Misc.patchMethodInfo (src, sri, types[0]);

            srf.add (sri);
        }
    }

    @Override
    public Object call (String namespace, String endpoint, String uuid, Object... args) throws ServiceNotFoundException, RemoteException {
        lookup (namespace, endpoint);

        ServiceRouteFrame srf = table.get (namespace);
        ServiceRouteItem sri = srf.get (endpoint);
        MethodInfo mi = sri.get (uuid);
        IRemoteServiceStub stub = RemoteServiceFactory.getRemoteService ();

        return stub.invoke (sri, mi, args);
    }

    protected void sendBroadcast (int what, Object arg) {
        if (logger.isTraceEnabled ()) {
            logger.trace ("sending a local broadcast over {}, what = {}, arg = {}", broadcast, what, arg);
        }
        if (broadcast != null) {
            broadcast.broadcast (Const.Category.CAT_SRF_EVENT, what, arg);
        }
    }
}
